package util;

import excecao.InfraestruturaException;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
 * A FabricaDeObjetos é para garantir que os objetos instanciados sejam únicos.
 * 
 */
public class FabricaDeObjetos {

    private static final String RESOURCE_BUNDLE_NAME = "classesFabrica";
    private static ResourceBundle prop;
    private static Map<String, Object> instanciados;

    static {
        try {
            prop = ResourceBundle.getBundle(RESOURCE_BUNDLE_NAME);
        } catch (MissingResourceException e) {
            System.out.println("Aquivo " + RESOURCE_BUNDLE_NAME + ".properties nao encontrado.");
            throw new InfraestruturaException(e);
        }

        instanciados = new HashMap<String, Object>();
    }

    /**
     * Retorna um objeto da classe <code>tipo</code>. Caso ainda 
     * não tenha sido criada uma instância da classe, ela é criada e retornada.
     * Caso a classe seja requisitada posteriormente, a mesma instância será 
     * retornada.
     * 
     * @param   tipo
     *          a classe do objeto a ser retornado
     *          hierarquia de pacotes
     * @return  uma instância da classe requisitada
     * @throws  ClassNotFoundException
     * @throws  InstantiationException
     * @throws  IllegalAccessException 
     */
    public static Object getObjetoUnico(Class tipo) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // Recupera-se o nome simples da classe (sem os pacotes) pois esta é a chave no arquivo .properties
        String nomeSimples = tipo.getSimpleName();

        // Procura-se uma instância existente
        Object resultado = instanciados.get(nomeSimples);

        if (resultado == null) {
            // Recupera-se do arquivo .properties o nome completo real da classe a ser criada
            String nomeDaClasseASerCriada = prop.getString(nomeSimples);

            resultado = Class.forName(nomeDaClasseASerCriada).newInstance();
            instanciados.put(nomeSimples, resultado);
        }

        return resultado;
    }

    /**
     * Armazena uma instância de um objeto se nenhuma daquela classe tiver sido 
     * criada ainda.
     * 
     * @param   instancia
     *          a instância a ser armazenada
     * @throws  Exception 
     *          se uma instância da classe já tiver sido criada
     */
    public static void putObjetoUnico(Object instancia) {
        // Recupera-se o nome simples da classe (sem os pacotes) pois esta é a chave no arquivo .properties
        String nomeSimples = instancia.getClass().getSimpleName();
        
        Object instanciaExistente = instanciados.get(nomeSimples);

        if (instanciaExistente != null) {
            throw new InfraestruturaException("Objeto já criado");
        } else {
            instanciados.put(instancia.getClass().getName(), instancia);
        }
    }
}
